package com.liu.proxy.core;

import com.liu.proxy.core.handler.IntegerTypeHandler;
import com.liu.proxy.core.handler.ParameterMappingTokenHandler;
import com.liu.proxy.core.handler.StringTypeHandler;
import com.liu.proxy.core.handler.TypeHandler;
import com.liu.proxy.jdbc.JDBCTools;
import com.liu.proxy.core.annotation.Param;
import com.liu.proxy.core.annotation.Select;
import com.liu.proxy.core.parameter.*;
import java.lang.reflect.*;
import java.sql.*;
import java.util.*;

/**
 * @author LIU
 * @version 1.0
 * @description TODO
 * @date 2024/1/15 18:06
 * mybatis 代理工厂
 */
public class MapperProxyFactory {
    private  static  Map<Class, TypeHandler> typeHandlerMap = new HashMap<>();


    static{
        // 类型处理器
        typeHandlerMap.put(String.class,new StringTypeHandler());
        typeHandlerMap.put(Integer.class,new IntegerTypeHandler());
    }


    public static <T> T getMapper(Class<T> mapper){

        Object instance = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{mapper}, new InvocationHandler() {
            /**
             * @author liuzq
             * @date 2024/1/16 9:46
             * @param proxy
             * @param method
             * @param args
             * @return Object
             * @description  解析SQL---> 执行SQL --->结果处理
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                // 1.获取连接
                Connection connection = JDBCTools.getConnection();

                // 1.1 参数解析
                Map<String, Object> paramValueMapping = new HashMap<>();
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Parameter parameter = parameters[i];
                    String name = parameter.getAnnotation(Param.class).value();
                    paramValueMapping.put(parameter.getName(),args[i]);
                    paramValueMapping.put(name,args[i]);
                }

                // 1.2 SQL解析
                Select annotation = method.getAnnotation(Select.class);
                String SQL = annotation.value(); // select * from user where name = #{name} and age > #{age}

                ParameterMappingTokenHandler tokenHandler = new ParameterMappingTokenHandler();
                GenericTokenParser parse = new GenericTokenParser("#{", "}", tokenHandler);
                String parseSQL = parse.parse(SQL); // select * from user where name = ? and age > ?

                // 2.获取链接
                PreparedStatement statement = connection.prepareStatement(parseSQL);


                // select * from user where name = 'tomcat' and age > 10
                List<ParameterMapping> parameterMappings = tokenHandler.getParameterMappings();
                for (int i = 0; i < parameterMappings.size(); i++) {
                    //  name,age
                    String property = parameterMappings.get(i).getProperty();
                    //  入参
                    Object value = paramValueMapping.get(property);

                    // 策略模式  设置入参  statement.setString(1,"tomcat");
                    Class<?> type = value.getClass();
                    typeHandlerMap.get(type).setParameter(statement,i+1,value);
                }



                // 3.执行SQL语句
                statement.execute();

                ResultSet resultSet = statement.getResultSet();
                ArrayList<Object> ObjectList = new ArrayList<>();

                // 获取返回值类型
                Class resultType = null;
                Type genericReturnType = method.getGenericReturnType();
                // 不是泛型 User
                if (genericReturnType instanceof Class){
                    resultType = (Class) genericReturnType;
                }
                // 是泛型  List<User>
                else if (genericReturnType instanceof ParameterizedType){
                    ParameterizedType parameterizedType = (ParameterizedType) genericReturnType;
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    resultType = (Class) actualTypeArguments[0];
                }

                // SQL中返回的字段
                ResultSetMetaData metaData = statement.getMetaData();
                ArrayList<String> columnList = new ArrayList<>();
                for (int i = 0; i < metaData.getColumnCount(); i++) {
                    //  id,name,age
                    columnList.add(metaData.getColumnName(i+1));
                }

                // 类set方法映射
                HashMap<String, Method> setterMethodMapping = new HashMap<>();
                for (Method declaredMethod : resultType.getDeclaredMethods()) {
                    //  setId
                    if (declaredMethod.getName().startsWith("set")){
                        // Id
                        String propertyName = declaredMethod.getName().substring(3);
                        // 首字母小写 id
                        propertyName = propertyName.substring(0, 1).toLowerCase(Locale.ROOT) + propertyName.substring(1);
                        setterMethodMapping.put(propertyName,declaredMethod);
                    }
                }


                // 返回值处理
                while (resultSet.next()){
                    // 一行数据
                    Object instance = resultType.newInstance();

                    for (int i = 0; i < columnList.size(); i++) {
                        String column = columnList.get(i);
                        Method setterMethod = setterMethodMapping.get(column);
                        Class<?> type = setterMethod.getParameterTypes()[0];
                        //返回值类型处理器
                        TypeHandler typeHandler = typeHandlerMap.get(type);

                        setterMethod.invoke(instance,typeHandler.getResult(resultSet,column));

//                    User user = new User();
//                    user.setId(resultSet.getInt("id"));
//                    user.setName(resultSet.getString("name"));
//                    user.setAge(resultSet.getInt("age"));
                    }
                    ObjectList.add(instance);
                }

                // 4.释放链接
                JDBCTools.free();

                // 返回值类型确定
                Object result;
                if (method.getReturnType().equals(List.class)){
                    result = ObjectList;
                }else {
                    result = ObjectList.get(0);
                }
                return result;
            }

        });

        return (T)instance;
    }
}
